package com.pcbly.web.components.utils.random;

import java.net.*;
import java.util.*;
import java.security.*;

import com.pcbly.web.components.utils.security.MD5Encoder;

/**
 * A random GUID (Globally Unique IDentifier) generator. GUID generation is
 * based on <a
 * href="http://www.javaexchange.com/aboutRandomGUID.html">RandomGUID.java</a>
 * from the Java Exchange.
 * <p>
 * <em>Original code author: Marc A. Mnich.:</em> In the multitude of java GUID
 * generators, I found none that guaranteed randomness. GUIDs are guaranteed to
 * be globally unique by using ethernet MACs, IP addresses, time elements, and
 * sequential numbers. GUIDs are not expected to be random and most often are
 * easy/possible to guess given a sample from a given generator. SQL Server, for
 * example generates GUID that are unique but sequencial within a given
 * instance.
 * </p>
 * <p>
 * GUIDs can be used as security devices to hide things such as files within a
 * filesystem where listings are unavailable (e.g. files that are served up from
 * a Web server with indexing turned off). This may be desireable in cases where
 * standard authentication is not appropriate. In this scenario, the RandomGUIDs
 * are used as directories. Another example is the use of GUIDs for primary keys
 * in a database where you want to ensure that the keys are secret. Random GUIDs
 * can then be used in a URL to prevent hackers (or users) from accessing
 * records by guessing or simply by incrementing sequential numbers.
 * </p>
 * <p>
 * There are many other possiblities of using GUIDs in the realm of security and
 * encryption where the element of randomness is important. This class was
 * written for these purposes but can also be used as a general purpose GUID
 * generator as well.
 * </p>
 * <p>
 * RandomGUID generates truly random GUIDs by using the system's IP address
 * (name/IP), system time in milliseconds (as an integer), and a very large
 * random number joined together in a single String that is passed through an
 * MD5 hash. The IP address and system time make the MD5 seed globally unique
 * and the random number guarantees that the generated GUIDs will have no
 * discernable pattern and cannot be guessed given any number of previously
 * generated GUIDs. It is generally not possible to access the seed information
 * (IP, time, random number) from the resulting GUIDs as the MD5 hash algorithm
 * provides one way encryption.
 * </p>
 * <p>
 * ----> Security of RandomGUID: <----- RandomGUID can be called one of two ways
 * -- with the basic java Random number generator or a cryptographically strong
 * random generator (SecureRandom). The choice is offered because the secure
 * random generator takes about 3.5 times longer to generate its random numbers
 * and this performance hit may not be worth the added security especially
 * considering the basic generator is seeded with a cryptographically strong
 * random seed.
 * </p>
 * <p>
 * Seeding the basic generator in this way effectively decouples the random
 * numbers from the time component making it virtually impossible to predict the
 * random number component even if one had absolute knowledge of the System
 * time. Thanks to Ashutosh Narhari for the suggestion of using the static
 * method to prime the basic random generator.
 * </p>
 * <p>
 * Using the secure random option, this class compies with the statistical
 * random number generator tests specified in FIPS 140-2, Security Requirements
 * for Cryptographic Modules, secition 4.9.1.
 * </p>
 * <p>
 * I converted all the pieces of the seed to a String before handing it over to
 * the MD5 hash so that you could print it out to make sure it contains the data
 * you expect to see and to give a nice warm fuzzy. If you need better
 * performance, you may want to stick to byte[] arrays.
 * </p>
 * <p>
 * I believe that it is important that the algorithm for generating random GUIDs
 * be open for inspection and modification. This class is free for all uses.
 * </p>
 * 
 */
public class RandomGuidGenerator  implements java.io.Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -6918493980399038963L;
	private static Random _rand;
	private static SecureRandom _secureRand;
	private static String _ipAddress = "1.2.3.4";

	/**
	 * Static block to take care of one time secureRandom seed. It takes a few
	 * seconds to initialize SecureRandom. You might want to consider removing
	 * this static block or replacing it with a "time since first loaded" seed
	 * to reduce this time. This block will run only once per JVM instance.
	 */
	static {
		_secureRand = new SecureRandom();
		long secureInitializer = _secureRand.nextLong();
		_rand = new Random(secureInitializer);

		try {
			InetAddress id = InetAddress.getLocalHost();
			_ipAddress = id.getHostAddress();

		} catch (Throwable t) {

		}

	}

	/**
	 * Generate a new GUID, indicating if it should be secure or not.
	 * <p>
	 * Secure GUIDs are more difficult to predict if previous GUIDs have already
	 * been seen.
	 * 
	 * @return the GUID in the standard format for GUIDs (eg:
	 *         8CD4AFAA-1B48-0A78-CF43-32014A25A0EB)
	 */
	public static String generateGuid(boolean secure) {

		long longPart = 0L;

		java.util.Random random = (secure) ? _secureRand : _rand;

		synchronized (RandomGuidGenerator.class) {
			longPart = random.nextLong();
		}

		long timePart = System.currentTimeMillis();
		StringBuffer buf = new StringBuffer(256);
		buf.append(_ipAddress);
		buf.append(':');
		buf.append(timePart);
		buf.append(':');
		buf.append(longPart);

		String preDigest = buf.toString();

		String postDigest = MD5Encoder.encode(MD5Encoder.digest(preDigest
				.getBytes()));

		// Convert to the standard format for GUID
		// (Useful for SQL Server UniqueIdentifiers, etc.)
		// Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
		String raw = postDigest.toUpperCase();
		buf = new StringBuffer(256);
		buf.append(raw.substring(0, 8));
		buf.append("-");
		buf.append(raw.substring(8, 12));
		buf.append("-");
		buf.append(raw.substring(12, 16));
		buf.append("-");
		buf.append(raw.substring(16, 20));
		buf.append("-");
		buf.append(raw.substring(20));

		return buf.toString();
	}

	public static String generateNotGuid(boolean secure) {

		long longPart = 0L;

		java.util.Random random = (secure) ? _secureRand : _rand;

		synchronized (RandomGuidGenerator.class) {
			longPart = random.nextLong();
		}

		long timePart = System.currentTimeMillis();
		StringBuffer buf = new StringBuffer(256);
		buf.append(_ipAddress);
		buf.append(':');
		buf.append(timePart);
		buf.append(':');
		buf.append(longPart);

		String preDigest = buf.toString();

		String postDigest = MD5Encoder.encode(MD5Encoder.digest(preDigest
				.getBytes()));

		// Convert to the standard format for GUID
		// (Useful for SQL Server UniqueIdentifiers, etc.)
		// Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
		String raw = postDigest.toUpperCase();

		return raw;
	}
public static void main(String[] args){
	System.out.println(RandomGuidGenerator.generateNotGuid(false));
	System.out.println(RandomGuidGenerator.generateGuid(false));
	System.out.println(RandomGuidGenerator.generateNotGuid(true));
	System.out.println(RandomGuidGenerator.generateGuid(true));
}
}
